package com.moo2u2.android.oaktononthego.util;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.util.EntityUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import android.content.Context;
import android.location.Address;
import android.location.Geocoder;
import android.util.Log;
import android.widget.Toast;

import com.moo2u2.android.oaktononthego.locate.Colleague;
import com.moo2u2.android.oaktononthego.locate.ColleagueOverlay;
import com.moo2u2.android.oaktononthego.locate.ColleagueOverlayItem;
import com.moo2u2.android.oaktononthego.shared.ColleagueProxy;

/**
 * Manager of Colleague events.
 * 
 * @author firewalker
 */
public class ColleagueManager {

    public static final String ADDED_EXTRA = "AddedExtra";

    public static final String COLLEAGUES_EXTRA = "ColleaguesExtra";

    public static final String COLLEAGUE_EXTRA = "ColleagueExtra";

    public static final String COLLEAGUES_SIZE_EXTRA = "ColleaguesSizeExtra";

    public static final String COLLEAGUES_URI = "https://oaktononthego.appspot.com/rest/colleagues";

    public static final String COLLEAGUES_FAKE_URI = "https://oaktononthego.appspot.com/rest/fakeColleagues";

    /**
     * Adds colleague data to the data source.
     * 
     * @param colleagues
     *            List of Colleagues to add
     * @return Server response
     * @throws ClientProtocolException
     *             If there's an error executing the request
     * @throws IOException
     *             If the response cannot be read
     */
    public static String addColleagues(List<ColleagueProxy> colleagues)
            throws ClientProtocolException, IOException {

        HttpClient client = new DefaultHttpClient(new BasicHttpParams());
        HttpPost post = new HttpPost(COLLEAGUES_FAKE_URI);
        HttpResponse response = client.execute(post);
        if (response.getStatusLine().getStatusCode() == 200) {
            String res = EntityUtils.toString(response.getEntity());
            return res;
        }

        throw new IOException("bad http response:"
                + response.getStatusLine().getReasonPhrase());
    }

    public static List<Colleague> getColleagues() {
        List<Colleague> result = new ArrayList<Colleague>();

        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(
                    COLLEAGUES_URI).openConnection();
            connection.setRequestMethod("GET");
            connection.setRequestProperty("Accept", "application/xml");

            InputStream xml = connection.getInputStream();
            Colleague colleague = null;

            Node node;
            Document doc;

            doc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
                    .parse(xml);

            XPath xpath = XPathFactory.newInstance().newXPath();
            NodeList nodes = (NodeList) xpath.evaluate("/colleagues/colleague",
                    doc, XPathConstants.NODESET);

            for (int i = 0; i < nodes.getLength(); i++) {
                node = nodes.item(i);
                colleague = new Colleague();
                try {
                    colleague.populateFromXml(node);
                } catch (NumberFormatException e) {
                    Log.e(ColleagueManager.class.getSimpleName(),
                            "Incorrect number", e);
                } catch (XPathExpressionException e) {
                    Log.e(ColleagueManager.class.getSimpleName(),
                            "XPath error", e);
                }
                Log.i(ColleagueManager.class.getSimpleName(),
                        colleague.toString());
                result.add(colleague);
            }
        } catch (SAXException e) {
            Log.e(ColleagueManager.class.getSimpleName(),
                    "Error building colleagues dom", e);
        } catch (ParserConfigurationException e) {
            Log.e(ColleagueManager.class.getSimpleName(),
                    "Error configuring dom parser", e);
        } catch (MalformedURLException e) {
            Log.e(ColleagueManager.class.getSimpleName(),
                    "Invalid url for fetching colleagues", e);
        } catch (IOException e) {
            Log.e(ColleagueManager.class.getSimpleName(),
                    "IO exception reading colleagues", e);
        } catch (XPathExpressionException e) {
            Log.e(ColleagueManager.class.getSimpleName(),
                    "Exception finding colleagues in XPath", e);
        }

        return result;
    }

    /**
     * Searches colleagues for given text. Tries name, description, address,
     * suburb.
     * 
     * TODO return a list and show / let select
     * 
     * @param ctx
     *            Context
     * @param colleagues
     *            List of colleagues to search
     * @param searchTxt
     *            Text on which to search.
     * @return Found colleague
     */
    public static ColleagueOverlayItem searchColleague(Context ctx,
            ColleagueOverlay colleagues, String searchTxt) {
        for (ColleagueOverlayItem colleague : colleagues.getColleagues()) {

            // Search name / data
            if (colleague.getTitle().toUpperCase()
                    .contains(searchTxt.toUpperCase())
                    || colleague.getSnippet().toUpperCase()
                            .contains(searchTxt.toUpperCase())) {
                Log.i(Util.class.getSimpleName(),
                        "Found: " + colleague.getTitle());
                return colleague;
            }

            // Search location
            Geocoder geocoder = new Geocoder(ctx, Locale.getDefault());
            try {
                List<Address> addresses = geocoder.getFromLocation(colleague
                        .getPoint().getLatitudeE6() / 1e6, colleague.getPoint()
                        .getLongitudeE6() / 1e6, 1);
                Address address = addresses.get(0);
                if (address != null) {
                    for (int addressLine = 0; addressLine < address
                            .getMaxAddressLineIndex(); addressLine++) {
                        if (address.getAddressLine(addressLine).toUpperCase()
                                .contains(searchTxt.toUpperCase()))
                            return colleague;
                    }
                    if (address.getLocality().toUpperCase()
                            .contains(searchTxt.toUpperCase()))
                        return colleague;
                    if (address.getSubLocality() != null
                            && address.getSubLocality().toUpperCase()
                                    .contains(searchTxt.toUpperCase()))
                        return colleague;
                }
            } catch (IOException e) {
                Log.e(Util.class.getSimpleName(), "Couldn't find address", e);
                Toast.makeText(ctx,
                        "Couldn't find address, please check your connection.",
                        Toast.LENGTH_SHORT).show();
            }
        }
        return null;
    }

}
